/*
** $Id: lua.h $
** Lua - A Scripting Language
** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
** See Copyright Notice at the end of this file
*/


#ifndef lua_h
#define lua_h

#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>


#include "luaconf.h"


#define LUA_VERSION_MAJOR	"5"
#define LUA_VERSION_MINOR	"4"
#define LUA_VERSION_RELEASE	"6"

#define LUA_VERSION_NUM			504
#define LUA_VERSION_RELEASE_NUM		(LUA_VERSION_NUM * 100 + 6)

#define LUA_VERSION	"Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
#define LUA_RELEASE	LUA_VERSION "." LUA_VERSION_RELEASE
#define LUA_COPYRIGHT	LUA_RELEASE "  Copyright (C) 1994-2023 Lua.org, PUC-Rio"
#define LUA_AUTHORS	"R. Ierusalimschy, L. H. de Figueiredo, W. Celes"


/* 预编译代码标记 ('<esc>Lua') */
#define LUA_SIGNATURE	"\x1bLua"

/* 'lua_pcall' 和 'lua_call'的多结果返回选项 */
#define LUA_MULTRET	(-1)


/*
** 伪索引
** (-LUAI_MAXSTACK 最小有效索引; 我们在这之后预留一些空间去帮助栈溢出检测
** LUA_REGISTRYINDEX 注册表索引
*/
#define LUA_REGISTRYINDEX	(-LUAI_MAXSTACK - 1000)
/* 返回当前运行的函数的第 i 个上值的伪索引 */
#define lua_upvalueindex(i)	(LUA_REGISTRYINDEX - (i))


/* 线程状态 */
#define LUA_OK		0 /* 正常返回 */
#define LUA_YIELD	1 /* 线程挂起  */
#define LUA_ERRRUN	2 /* 运行时错误 */
#define LUA_ERRSYNTAX	3 /* 语法错误 */
#define LUA_ERRMEM	4 /* 内存错误 */
#define LUA_ERRERR	5 /* 运行错误消息处理器时发生错误 */


typedef struct lua_State lua_State;


/*
** 基础类型
*/
#define LUA_TNONE		(-1)

#define LUA_TNIL		0
#define LUA_TBOOLEAN		1
#define LUA_TLIGHTUSERDATA	2
#define LUA_TNUMBER		3
#define LUA_TSTRING		4
#define LUA_TTABLE		5
#define LUA_TFUNCTION		6
#define LUA_TUSERDATA		7
#define LUA_TTHREAD		8

#define LUA_NUMTYPES		9



/* 可用于C函数的最小lua栈，无论何时Lua调用C，都至少有这么多堆栈空间可以使用 */
#define LUA_MINSTACK	20


/* 注册表：用来保存任何C代码想保存的Lua值 */
/* 注册表中状态机主线程的索引 */
#define LUA_RIDX_MAINTHREAD	1
/* 注册表中全局环境的索引 */
#define LUA_RIDX_GLOBALS	2
#define LUA_RIDX_LAST		LUA_RIDX_GLOBALS


/* Lua中的number类型 */
typedef LUA_NUMBER lua_Number;


/* lua中的integer类型 */
typedef LUA_INTEGER lua_Integer;

/* unsigned integer类型 */
typedef LUA_UNSIGNED lua_Unsigned;

/* 延续函数上下文类型 */
typedef LUA_KCONTEXT lua_KContext;


/*
** 在Lua中注册的C函数的类型
** 为了正确的和 Lua 通讯， C 函数必须使用下列协议
** 这个协议定义了参数以及返回值传递方法： C 函数通过 Lua 中的栈来接受参数， 参数以正序入栈（第一个参数首先入栈）。
** 因此，当函数开始的时候， lua_gettop(L) 可以返回函数收到的参数个数。 第一个参数（如果有的话）在索引 1 的地方， 而最后一个参数在索引 lua_gettop(L) 处
** 当需要向 Lua 返回值的时候， C 函数只需要把它们以正序压到堆栈上（第一个返回值最先压入），然后返回返回值的个数
** 在这些返回值之下的，堆栈上的东西都会被 Lua 丢弃。 和 Lua 函数一样，从 Lua 中调用 C 函数也可以有很多返回值
*/
typedef int (*lua_CFunction) (lua_State *L);

/*
** 延续函数类型
*/
typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx);


/*
** 读取和写入Lua程序块时的读写函数类型
*/
typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);

typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud);


/*
** 内存分配函数类型
** ud: 由lua_newstate传递的指针
** ptr: 一个指向已分配出来/将被重新分配/要释放的内存块指针
** osize: 内存块原来的尺寸或是关于什么将被分配出来(类型)的代码
** nsize: 新内存块的尺寸
*/
typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);


/*
** 警告函数类型
*/
typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont);


/*
** 用于debugAPI收集debug信息的类型
*/
typedef struct lua_Debug lua_Debug;


/*
** 用于debug的钩子函数类型
*/
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);


/*
** 额外include的文件
*/
#if defined(LUA_USER_H)
#include LUA_USER_H
#endif


/*
** RCS 识别字符串
*/
extern const char lua_ident[];


/*
** state操作
*/
/* 创建一个运行在新的独立状态机中的线程，如果无法创建则返回NULL，ud将在每次调用分配函数时传入ud */
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
/* 关闭指定lua_State，并释放其中的内存 */
LUA_API void       (lua_close) (lua_State *L);
/* 创建一条新线程，并将其压栈，并返回维护这个线程的 lua_State 指针。这个函数返回的新线程共享原线程的全局环境，但是它有独立的运行栈。 */
LUA_API lua_State *(lua_newthread) (lua_State *L);
LUA_API int        (lua_closethread) (lua_State *L, lua_State *from);
LUA_API int        (lua_resetthread) (lua_State *L);  /* 已弃用! */

LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);


LUA_API lua_Number (lua_version) (lua_State *L);


/*
** 基本堆栈操作
*/
LUA_API int   (lua_absindex) (lua_State *L, int idx);
/* 获取栈顶索引，也是栈的元素个数 */
LUA_API int   (lua_gettop) (lua_State *L);
/* 设置栈顶索引，如果比原来大，则超出位置设置为nil，否则删除多余的索引 */
LUA_API void  (lua_settop) (lua_State *L, int idx);
/* 把栈上给定索引处的元素作一个副本压栈 */
LUA_API void  (lua_pushvalue) (lua_State *L, int idx);
/* 把idx索引到栈顶元素旋转n个位置 */
LUA_API void  (lua_rotate) (lua_State *L, int idx, int n);
/* 将fromidx索引的值赋值到toidx索引 */
LUA_API void  (lua_copy) (lua_State *L, int fromidx, int toidx);
/* 确保栈上有n个额外空位,如果不能把堆栈扩展到对应尺寸，返回假，这个函数不会缩小栈 */
LUA_API int   (lua_checkstack) (lua_State *L, int n);

/*
** 交换同一个状态机下不同线程中的值
** 这个函数会从 from 的栈上弹出 n 个值， 然后把它们压入 to 的栈上
*/
LUA_API void  (lua_xmove) (lua_State *from, lua_State *to, int n);


/*
** 访问函数 (stack -> C)
*/

/* idx索引是否是number类型 */
LUA_API int             (lua_isnumber) (lua_State *L, int idx);
/* idx索引是否是string类型 */
LUA_API int             (lua_isstring) (lua_State *L, int idx);
/* idx索引是否是C函数 */
LUA_API int             (lua_iscfunction) (lua_State *L, int idx);
/* idx索引是否是integer */
LUA_API int             (lua_isinteger) (lua_State *L, int idx);
/* idx索引是否是userdata */
LUA_API int             (lua_isuserdata) (lua_State *L, int idx);
/* 获取idx索引的值类型 */
LUA_API int             (lua_type) (lua_State *L, int idx);
/* 根据type的数字获取type名字 */
LUA_API const char     *(lua_typename) (lua_State *L, int tp);

/* 将idx索引处的值转换为带符号的lua_Number, is_num不是NULL则会被设为操作是否成功 */
LUA_API lua_Number      (lua_tonumberx) (lua_State *L, int idx, int *isnum);
/* 将idx索引处的值转换为带符号的lua_Integer, is_num不是NULL则会被设为操作是否成功 */
LUA_API lua_Integer     (lua_tointegerx) (lua_State *L, int idx, int *isnum);
/* 将idx索引处的值转换成一个bool */
LUA_API int             (lua_toboolean) (lua_State *L, int idx);
/* 将idx索引处的值转换为string，len不为NULL则设为字符串长度(lstring: 不以\0作为字符串结尾，可以包含\0) */
LUA_API const char     *(lua_tolstring) (lua_State *L, int idx, size_t *len);
/*
** 获取idx索引处值的长度
** string|table: #操作结果
** useerdata: 数据分配的内存大小
** 其他: 0
*/
LUA_API lua_Unsigned    (lua_rawlen) (lua_State *L, int idx);
/* 将idx处索引值转换成一个C函数 */
LUA_API lua_CFunction   (lua_tocfunction) (lua_State *L, int idx);
/* 将idx处索引值转换成一个userdata */
LUA_API void	       *(lua_touserdata) (lua_State *L, int idx);
/* 把给定索引处的值转换为一个 Lua 线程 （表示为 lua_State*）。 这个值必须是一个线程； 否则函数返回 NULL */
LUA_API lua_State      *(lua_tothread) (lua_State *L, int idx);
/* 将idx索引处的值转换成C指针 */
LUA_API const void     *(lua_topointer) (lua_State *L, int idx);


/*
** 比较和算数方法
*/

#define LUA_OPADD	0	/* ORDER TM, ORDER OP， + */
#define LUA_OPSUB	1 /* 减法(-) */
#define LUA_OPMUL	2 /* 乘法(*) */
#define LUA_OPMOD	3 /* 取模 (%) */
#define LUA_OPPOW	4 /* 乘方(^) */
#define LUA_OPDIV	5 /* 除法(/) */
#define LUA_OPIDIV	6 /* 向下取整除法 (//) */
#define LUA_OPBAND	7 /* 按位与 (&) */
#define LUA_OPBOR	8 /* 按位或 (|) */
#define LUA_OPBXOR	9 /* 按位异或(~) */
#define LUA_OPSHL	10  /* 左移 (<<) */
#define LUA_OPSHR	11  /* 右移 (>>) */
#define LUA_OPUNM	12  /* 取负 (一元 -) */
#define LUA_OPBNOT	13 /* 按位取反 (~) */

// 对栈顶的两个值做一次数学或位操作
LUA_API void  (lua_arith) (lua_State *L, int op);

/* 比较操作标记 */
#define LUA_OPEQ	0 /* == */
#define LUA_OPLT	1 /* < */
#define LUA_OPLE	2 /* <= */

/* 比较idx1和idx索引位置的值是否相等(不触发元方法)(raw表示不会调用元方法) */
LUA_API int   (lua_rawequal) (lua_State *L, int idx1, int idx2);
/* 比较idx1和idx2索引位置的两个值，如果 ```idx1处的值 op idx2处的值``` 返回 0  */
LUA_API int   (lua_compare) (lua_State *L, int idx1, int idx2, int op);


/*
** 压入函数 (C -> stack)
*/
/* 压入一个nil */
LUA_API void        (lua_pushnil) (lua_State *L);
/* 压入一个number */
LUA_API void        (lua_pushnumber) (lua_State *L, lua_Number n);
/* 压入一个integer */
LUA_API void        (lua_pushinteger) (lua_State *L, lua_Integer n);
/* 压入一个字符串(s所指向的长度为len的字符串) */
LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len);
/* 压入一个字符串(C中的字符串,\0结尾) */
LUA_API const char *(lua_pushstring) (lua_State *L, const char *s);
/* 压入一个格式化字符串 */
LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
                                                      va_list argp);
/* 把一个格式化过的字符串压入栈，然后返回这个字符串的指针 */
LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
/*
** 当创建了一个 C 函数后， 你可以给它关联一些值， 这就是在创建一个 C 闭包
** 接下来无论函数何时被调用，这些值都可以被这个函数访问到。为了将一些值关联到一个 C 函数上，首先这些值需要先被压入堆栈（如果有多个值，第一个先压）
** 接下来调用 lua_pushcclosure 来创建出闭包并把这个 C 函数压到栈上。 参数 n 告之函数有多少个值需要关联到函数上
** lua_pushcclosure 也会把这些值从栈上弹出
*/
LUA_API void  (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
/* 把n作为bool压入栈 */
LUA_API void  (lua_pushboolean) (lua_State *L, int b);
/* 压入一个轻量userdata */
LUA_API void  (lua_pushlightuserdata) (lua_State *L, void *p);
/* 将L表示的线程压入栈，如果这个线程是当前状态机的主线程则返回1 */
LUA_API int   (lua_pushthread) (lua_State *L);


/*
** get functions (Lua -> stack)
*/
/* 将全局变量name的值压入栈中，并返回值的类型 */
LUA_API int (lua_getglobal) (lua_State *L, const char *name);
/* 把 t[k] 的值压栈，t 是idx指向的值， 而 k 则是栈顶放的值 */
LUA_API int (lua_gettable) (lua_State *L, int idx);
/* 将idx索引位置的 table的k键的值(t[k]) 压入栈，返回压入值的类型 */
LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k);
/* 将t[n]压入栈中，t在idx索引 */
LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n);
/* 不触发元方法将 t[k] 的值压栈，t 是idx指向的值，而 k 则是栈顶放的值 */
LUA_API int (lua_rawget) (lua_State *L, int idx);
/* 不触发元方法将 t[n] 压入栈中,t在idx索引 */
LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n);
/* 不触发元方法将 t[k] 压入栈中，t在idx索引，k是指针p对应的light userdata */
LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p);

/*
** 创建一张新的空表压栈
** narr: 建议这张表作为序列时会有多少元素
** nrec: 建议这张表可能拥有过多少序列之外的元素
*/
LUA_API void  (lua_createtable) (lua_State *L, int narr, int nrec);
LUA_API void *(lua_newuserdatauv) (lua_State *L, size_t sz, int nuvalue);
/* 如果objindex索引的值有元表，将其压入栈，返回1，否则返回0 */
LUA_API int   (lua_getmetatable) (lua_State *L, int objindex);
LUA_API int  (lua_getiuservalue) (lua_State *L, int idx, int n);


/*
** set functions (stack -> Lua)
*/
/* 将栈顶值设置成全局变量name的值 */
LUA_API void  (lua_setglobal) (lua_State *L, const char *name);
/* t[k] = v， t 是给出的索引处的值， v 是栈顶的那个值， k 是栈顶之下的值。 */
LUA_API void  (lua_settable) (lua_State *L, int idx);
/* t[k] = v, 将idx索引处的表设置其t[k]为栈顶元素，会将栈顶值弹出栈 */
LUA_API void  (lua_setfield) (lua_State *L, int idx, const char *k);
/* t[n] = v, 将idx索引处的表设置其t[n]为栈顶元素，会将栈顶值弹出栈 */
LUA_API void  (lua_seti) (lua_State *L, int idx, lua_Integer n);
/* t[k] = v， t 是给出的索引处的值，v 是栈顶的那个值，k 是栈顶之下的值。 */
LUA_API void  (lua_rawset) (lua_State *L, int idx);
/* t[n] = v, 将idx索引处的表设置其t[n]为栈顶元素，会将栈顶值弹出栈 */
LUA_API void  (lua_rawseti) (lua_State *L, int idx, lua_Integer n);
/* t[k] = v, 将idx索引处的表设置其t[p]为栈顶元素，会将栈顶的值弹出栈，p是light userdata */
LUA_API void  (lua_rawsetp) (lua_State *L, int idx, const void *p);
LUA_API int   (lua_setmetatable) (lua_State *L, int objindex);
LUA_API int   (lua_setiuservalue) (lua_State *L, int idx, int n);


/*
** 'load' and 'call' functions (load and run Lua code)
** nargs: 参数个数
** nresults: 返回值个数
** ctx: 可延续上下文
** k: 可延续函数
*/
LUA_API void  (lua_callk) (lua_State *L, int nargs, int nresults,
                           lua_KContext ctx, lua_KFunction k);
#define lua_call(L,n,r)		lua_callk(L, (n), (r), 0, NULL)

/* 以保护模式运行函数，并允许函数让出, nargs: 参数个数，nresults: 返回结果个数, errfunc: 错误处理函数的栈索引， ctx: 可延续函数上下文， k: 可延续函数 */
LUA_API int   (lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc,
                            lua_KContext ctx, lua_KFunction k);
/* 以保护模式运行函数, n: 参数个数, r: 结果个数, f: 错误处理方法 */
#define lua_pcall(L,n,r,f)	lua_pcallk(L, (n), (r), (f), 0, NULL)

/* 加载一段 Lua 代码块，但不运行它。 如果没有错误，lua_load 把一个编译好的代码块作为一个 Lua 函数压到栈顶。 否则，压入错误消息。 */
LUA_API int   (lua_load) (lua_State *L, lua_Reader reader, void *dt,
                          const char *chunkname, const char *mode);

/*
** 将函数导出成二进制代码
** 接受栈顶的Lua函数做参数，然后生成它的二进制代码块，若被导出的东西被再次加载，加载结果相当于原来的函数
** strip: 为true，二进制代码不包含该函数调试信息
*/
LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data, int strip);


/*
** 协程方法
*/
/* 让出协程，返回nresults个结果 */
LUA_API int  (lua_yieldk)     (lua_State *L, int nresults, lua_KContext ctx,
                               lua_KFunction k);
/* 在给定线程中启动或继续一个线程 */
LUA_API int  (lua_resume)     (lua_State *L, lua_State *from, int narg,
                               int *nres);
/* 返回线程L的状态 */
LUA_API int  (lua_status)     (lua_State *L);
/* 给定协程是否可以让出 */
LUA_API int (lua_isyieldable) (lua_State *L);

#define lua_yield(L,n)		lua_yieldk(L, (n), 0, NULL)


/*
** Warning-related functions
*/
LUA_API void (lua_setwarnf) (lua_State *L, lua_WarnFunction f, void *ud);
LUA_API void (lua_warning)  (lua_State *L, const char *msg, int tocont);


/*
** gc方法和选项
*/

#define LUA_GCSTOP		0 /* 停止垃圾回收器 */
#define LUA_GCRESTART		1 /* 重启垃圾回收期 */
#define LUA_GCCOLLECT		2 /* 发起一次垃圾回收 */
#define LUA_GCCOUNT		3 /* 返回Lua使用的内存总量 */
#define LUA_GCCOUNTB		4 /* 返回当前内存使用量/1024的余数*/
#define LUA_GCSTEP		5 /* 发起一步增量gc */
#define LUA_GCSETPAUSE		6 /* 设置gc的间歇率，并返回之前的值 */
#define LUA_GCSETSTEPMUL	7 /* 设置gc步进倍率，并返回之前的值 */
#define LUA_GCISRUNNING		9 /* 返回gc是否在运行 */
#define LUA_GCGEN		10
#define LUA_GCINC		11

/*
** gc 函数
** 根据what做不同操作
*/
LUA_API int (lua_gc) (lua_State *L, int what, ...);


/*
** 其他函数
*/

/* 将栈顶值作为错误对象抛出一个错误，会调用一次longjmp，所以一定不会返回 */
LUA_API int   (lua_error) (lua_State *L);

/* 从栈顶弹出一个键，然后把idx索引处的表中的一个键值对压栈（弹出的键之后的 “下一” 对）。 如果表中以无更多元素， 那么 lua_next 将返回 0（什么也不压栈）。 */
LUA_API int   (lua_next) (lua_State *L, int idx);

/* 连接栈顶的n个值 */
LUA_API void  (lua_concat) (lua_State *L, int n);
/* idx索引的值的长度，等价于Lua中的 # 操作符，将结果压入栈中 */
LUA_API void  (lua_len)    (lua_State *L, int idx);

/* 将一个字符串转换为一个数字，并压入栈中，返回字符串总长度 */
LUA_API size_t   (lua_stringtonumber) (lua_State *L, const char *s);

/* 获取内存分配函数，if (ud) *ud = G(L)->ud;*/
LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
/* 设置内存分配函数 */
LUA_API void      (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);

/* 将给定索引标记为一个待关闭插槽，不应该对等于或小于一个活动的待关闭的插槽索引调用此函数 */
LUA_API void (lua_toclose) (lua_State *L, int idx);
LUA_API void (lua_closeslot) (lua_State *L, int idx);


/*
** {==============================================================
** 一些实用的宏
** ===============================================================
*/

/*
** 返回一个 Lua 状态机关联的内存块指针。程序可以把这块内存用于任何用途；而 Lua 不会使用它
** 每一个新线程都会携带一块内存，初始化为主线程的这块内存的副本
** 默认配置下，这块内存的大小为空指针的大小。不过你可以重新编译 Lua 设定这块内存不同的大小。（参见 luaconf.h 中的 LUA_EXTRASPACE）
*/
#define lua_getextraspace(L)	((void *)((char *)(L) - LUA_EXTRASPACE))

#define lua_tonumber(L,i)	lua_tonumberx(L,(i),NULL)
#define lua_tointeger(L,i)	lua_tointegerx(L,(i),NULL)

/* 从栈中弹出n个元素 */
#define lua_pop(L,n)		lua_settop(L, -(n)-1)
/* 创建一个空表压入栈 */
#define lua_newtable(L)		lua_createtable(L, 0, 0)

/*
** void lua_register (lua_State *L, const char *name, lua_CFunction f);
** 把C函数设置到全局变量name
*/
#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))

/* 将c函数压入栈 lua_pushcfunction(L, lua_CFunction f) */
#define lua_pushcfunction(L,f)	lua_pushcclosure(L, (f), 0)

/* n索引位置是否是function类型 */
#define lua_isfunction(L,n)	(lua_type(L, (n)) == LUA_TFUNCTION)
/* n索引位置是否是table类型 */
#define lua_istable(L,n)	(lua_type(L, (n)) == LUA_TTABLE)
/* n索引位置是否是light userdata类型 */
#define lua_islightuserdata(L,n)	(lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
/* n索引位置是否是nil */
#define lua_isnil(L,n)		(lua_type(L, (n)) == LUA_TNIL)
/* n索引位置是否是bool类型 */
#define lua_isboolean(L,n)	(lua_type(L, (n)) == LUA_TBOOLEAN)
/* n索引位置是否是thread类型 */
#define lua_isthread(L,n)	(lua_type(L, (n)) == LUA_TTHREAD)
/* n索引是否是None */
#define lua_isnone(L,n)		(lua_type(L, (n)) == LUA_TNONE)
/* n索引位置是否是None或是nil */
#define lua_isnoneornil(L, n)	(lua_type(L, (n)) <= 0)

/* 将字符串字面量压入栈 */
#define lua_pushliteral(L, s)	lua_pushstring(L, "" s)

/* 将全局环境压入栈 */
#define lua_pushglobaltable(L)  \
	((void)lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS))

#define lua_tostring(L,i)	lua_tolstring(L, (i), NULL)

/* 把栈顶元素移动到idx索引，不要用伪索引来调用，因为伪索引没有真正指向栈上的位置 */
#define lua_insert(L,idx)	lua_rotate(L, (idx), 1)

/* 将idx索引移除，不能使用伪索引 */
#define lua_remove(L,idx)	(lua_rotate(L, (idx), -1), lua_pop(L, 1))
/* 使用栈顶的值来替换掉idx索引处的值，这个函数会将栈顶值弹出 */
#define lua_replace(L,idx)	(lua_copy(L, -1, (idx)), lua_pop(L, 1))

/* }============================================================== */


/*
** {==============================================================
** 兼容性宏
** ===============================================================
*/
#if defined(LUA_COMPAT_APIINTCASTS)

#define lua_pushunsigned(L,n)	lua_pushinteger(L, (lua_Integer)(n))
#define lua_tounsignedx(L,i,is)	((lua_Unsigned)lua_tointegerx(L,i,is))
#define lua_tounsigned(L,i)	lua_tounsignedx(L,(i),NULL)

#endif

/* 分配一款指定大小的内存块，把内存地址作为一个userdata压入栈，并返回地址 */
#define lua_newuserdata(L,s)	lua_newuserdatauv(L,s,1)
/* 将idx索引的userdata所关联的Lua值压入栈 */
#define lua_getuservalue(L,idx)	lua_getiuservalue(L,idx,1)
/* 从栈上弹出一个值并将其设为给定索引处用户数据的关联值 */
#define lua_setuservalue(L,idx)	lua_setiuservalue(L,idx,1)

#define LUA_NUMTAGS		LUA_NUMTYPES

/* }============================================================== */

/*
** {======================================================================
** 调试API
** =======================================================================
*/


/*
** 事件码
*/
#define LUA_HOOKCALL	0
#define LUA_HOOKRET	1
#define LUA_HOOKLINE	2
#define LUA_HOOKCOUNT	3
#define LUA_HOOKTAILCALL 4


/*
** 事件掩码
*/
#define LUA_MASKCALL	(1 << LUA_HOOKCALL) /* 函数被调用时 */
#define LUA_MASKRET	(1 << LUA_HOOKRET)  /* 执行结束返回时 */
#define LUA_MASKLINE	(1 << LUA_HOOKLINE) /* 每一行执行完后 */
#define LUA_MASKCOUNT	(1 << LUA_HOOKCOUNT)  /* 每次执行统计运行次数 */


/* 获取解释器的运行时栈的信息，level表示层，0：当前运行函数，n+1：调用第n层函数的栈 */
LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar);
/* 返回指定函数或函数调用的信息 */
LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar);
/* 获取函数中n的局部变量的信息 */
LUA_API const char *(lua_getlocal) (lua_State *L, const lua_Debug *ar, int n);
/* 设置函数中的局部变量的信息 */
LUA_API const char *(lua_setlocal) (lua_State *L, const lua_Debug *ar, int n);
/* 获取第n个上值，压入栈，并返回他的名字，funcindex表示闭包在栈上的位置 */
LUA_API const char *(lua_getupvalue) (lua_State *L, int funcindex, int n);
/* 设置第n个上值，压入栈，并返回他的名字，funcindex表示闭包在栈上的位置，值在栈顶 */
LUA_API const char *(lua_setupvalue) (lua_State *L, int funcindex, int n);

/* 返回 funcindex 处的闭包中 编号为 n 的上值的一个唯一标识符(指针) */
LUA_API void *(lua_upvalueid) (lua_State *L, int fidx, int n);
/* 让索引 funcindex1 处的 Lua 闭包的第 n1 个上值 引用索引 funcindex2 处的 Lua 闭包的第 n2 个上值 */
LUA_API void  (lua_upvaluejoin) (lua_State *L, int fidx1, int n1,
                                               int fidx2, int n2);

/* 设置调试钩子函数 */
LUA_API void (lua_sethook) (lua_State *L, lua_Hook func, int mask, int count);
/* 获取当前钩子函数 */
LUA_API lua_Hook (lua_gethook) (lua_State *L);
/* 获取当前钩子掩码 */
LUA_API int (lua_gethookmask) (lua_State *L);
/* 获取当前钩子计数器 */
LUA_API int (lua_gethookcount) (lua_State *L);

LUA_API int (lua_setcstacklimit) (lua_State *L, unsigned int limit);

struct lua_Debug {
  int event;
  const char *name;	/* (n) 函数名字 */
  const char *namewhat;	/* (n) 'global', 'local', 'field', 'method' */
  const char *what;	/* (S) 函数类型, 'Lua', 'C', 'main', 'tail' */
  const char *source;	/* (S) */
  size_t srclen;	/* (S) */
  int currentline;	/* (l) 当前运行行号 */
  int linedefined;	/* (S) 函数定义开始处的行号 */
  int lastlinedefined;	/* (S) 函数定义结束处的行号 */
  unsigned char nups;	/* (u) 上值数量 */
  unsigned char nparams;/* (u) 参数数量(对于C函数总是为0) */
  char isvararg;        /* (u) 是否可变参数(对于C函数总是为true) */
  char istailcall;	/* (t) 是否尾调用 */
  unsigned short ftransfer;   /* (r) 第一个被转移值的索引 */
  unsigned short ntransfer;   /* (r) 转移值的个数 */
  char short_src[LUA_IDSIZE]; /* (S) */
  /* 私有部分 */
  struct CallInfo *i_ci;  /* 活跃的函数 */
};

/* }====================================================================== */


/******************************************************************************
* Copyright (C) 1994-2023 Lua.org, PUC-Rio.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/


#endif
